/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: future.h
 *
 * Purpose: implementation future functional
 *
 * Developer:
 *   wen.gu , 2020-04-10
 *
 * TODO:
 *
 ***************************************************************************/

#ifndef __CORE_COMPONENT_H__
#define __CORE_COMPONENT_H__
/******************************************************************************
 **    INCLUDES
 ******************************************************************************/

#include <chrono>
#include <future>
#include <type_traits>

#include "ara/core/error_code.h"
#include "ara/core/result.h"
#include "ara/core/future_error_domain.h"
#include "ara/core/internal/future_internal.h"
#include "ara/core/utility.h"

/******************************************************************************
 **    MACROS
 ******************************************************************************/


/******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
namespace ara
{
namespace core
{

/** Specifies the state of a Future as returned by wait_for() and wait_until().
 *  These definitions are equivalent to the ones from std::future_status. However, no item
 *  equivalent to std::future_status::deferred is available here.
 *  The numerical values of the enum items are implementation-defined
*/
enum class future_status : uint8_t
{
    ready, /** the shared state is ready */
    timeout, /** the shared state did not become ready before the specified timeout has passed */
};

/******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/
template <typename T , typename E>
class Promise;


namespace internal_impl
{
/** for convert future error type */
constexpr future_status Convert2StdStatus(std::future_status st)
{
    future_status ret = future_status::timeout;
    switch (st)
    {
    case std::future_status::deferred: /** todo refine me?? */
    case std::future_status::timeout:
        ret = future_status::timeout;
        break;
    case std::future_status::ready:
        ret = future_status::ready;
        break;
    default:
        break;
    }

    return ret;
}
}

/**
 * Provides ara::core specific Future operations to collect the results of an asynchronous call
 */
template <typename T, typename E = ErrorCode>
class Future final
{
public:
    using value_type = Result<T, E>;
    using value_ptr = std::shared_ptr<value_type>;
    using future_type = std::future<value_ptr>;
    using internal_sate_type = internal_impl::FutureInternalState;
    using state_ptr = std::shared_ptr<internal_sate_type>;
public:
    Future () noexcept=default;
    Future (Future &&other) noexcept
        :mFuture(std::move(other.mFuture)),
         mInnerState(std::move(other.mInnerState))        
    {
        /** todo something */
    }

    ~Future ()
    {
        /** todo something */
    }


public:
    Future& operator= (Future &&other)
    {
        mFuture = std::move(other.mFuture);
        mInnerState = std::move(other.mInnerState);
    }

public:
    T get ()
    {
        return GetResult().ValueOrThrow();
    }

    Result<T, E> GetResult () noexcept
    {
        try
        {
            return std::move(*(mFuture.get().get()));
        }
        catch (std::future_error const& err)
        {
            const std::error_code& errc = err.code();
            future_errc ferr = future_errc::no_state;
            if (errc == std::future_errc::broken_promise)
            {
                ferr = future_errc::broken_promise;
            }
            else if (errc == std::future_errc::future_already_retrieved)
            {
                ferr = future_errc::future_already_retrieved;
            }
            else if (errc == std::future_errc::no_state)
            {
                ferr = future_errc::no_state;
            }
            else if (errc == std::future_errc::promise_already_satisfied)
            {
                ferr = future_errc::promise_already_satisfied;
            }
            else
            {
                /** tofo refine.*/
                //return value_type::FromError()
            }

            return std::move(value_type::FromError(ferr));
        }
    }

    bool valid () const noexcept
    {
        return mFuture.valid();
    }

    bool is_ready () const
    {
        return (mFuture.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready);
    }


public:
    void wait () const
    {
        mFuture.wait();
    }

    template <typename Rep, typename Period>
    future_status wait_for(std::chrono::duration< Rep, Period > const &timeoutDuration) const
    {
        return internal_impl::Convert2StdStatus(mFuture.wait_for(timeoutDuration));
    }

    template <typename Clock, typename Duration>
    future_status wait_until(std::chrono::time_point< Clock, Duration > const &deadline) const
    {
        return internal_impl::Convert2StdStatus(mFuture.wait_until(deadline));
    }


/** now implmentation this member function only when c++ standard >= c++14,
 *  todo, fixme, is need implementation for c++11
 */
#if 1 //now all case is valid if >= c++11, todo,is need do this __cplusplus >= 201402L


    /**@brief Register a callable(callback function) that gets called when the Future becomes ready.
     * When func is called, it is guaranteed that get() and GetResult() will not block.
     * func may be called in the context of this call or in the context of Promise::set_value() or
     * Promise::SetError() or somewhere else.
     * The return type of then depends on the return type of func (aka continuation).

     *the F type maybe:
     * - Future<T2, E2> F(Future<T, E>&& f)
     * - Result<T2, E2> F(Future<T, E>&& f)
     * - xxx            F(Future<T, E>&& f)
     */

    /** register a communication function(only called once)   */
#if 1       /**todo, refine me?? now always return Future<decltype(func(std::move(*this)))> */
    template <typename F>
    auto then(F&& func) ->Future<decltype(func(std::move(*this)))>
    { 
        /** for the case of: Future<T2, E2> F(Future<T, E>&& f)
           *  result of then() is: Future<Future<T2, E2>>
           * 
           *for the case of: Result<T2, E2> F(Future<T, E>&& f)
           *  result of then() is: Future<Result<T2, E2>>
           *
           * for the case of: xxx F(Future<T, E>&& f)
           *  result of then() is: Future<xxx>
           */
        using type = decltype(func(std::move(*this)));
        std::shared_ptr<Promise<type, E>> prom = std::make_shared<Promise<type, E>>();
        Future<type> fu = prom->get_future();

        Future<T>* curFu = new Future<T>(std::move(mFuture), mInnerState);
        /** todo refineme, check mInnerState is valid?? */
        mInnerState->setCommunicationFunc([curFu, func, prom]() {
    
                prom->set_value(func(std::move(*curFu)));
                delete curFu;
            });

        return fu;
    }
#else

#if 1
    template <typename F>
     auto then(F&& func) -> decltype(Future<FUTURE_FUNC_RET_TYPES::type_t, FUTURE_FUNC_RET_TYPES::type_e>)
    {
         /** for the case of: Future<T2, E2> F(Future<T, E>&& f)
                   *  result of then() is: Future<T2, E2>
                   */
         using rvt = decltype(FUTURE_FUNC_RET_TYPES::type_t); /** return value type(T2) */
         using ret = decltype(FUTURE_FUNC_RET_TYPES::type_e); /** return error type(E2) */
         using promt = Promise<rvt, ret>; /** promise type */
         std::shared_ptr<promt> prom = std::make_shared<promt>();
         Future<rvt, ret> fu = prom->get_future();

         std::shared_ptr < Future<T, E> > curFu = new Future(std::move(mFuture), mInnerState);
         /** todo refineme, check mInnerState is valid?? */
         mInnerState->setCommunicationFunc([curFu, func, prom]() {

             Future<rvt, ret> fret = func(std::move(curFu.get()));
             Result<rvt, ret> res = fret.GetResult();
             if (res.HasValue())
             {
                 prom->set_value(std::move(res.Value()));
             }
             else
             {
                 prom->SetError(std::move(res.Error()));
             }
             });

         return fu;
    }
#else
    template <typename F, typename std::enable_if<internal_impl::is_template_of<Future, std::result_of<std::declval(F)()(std::move(*this))>::type >::value, bool>::type = true >
    auto then(F&& func) -> decltype(func(std::move(*this)))
    {
        /** for the case of: Future<T2, E2> F(Future<T, E>&& f)
           *  result of then() is: Future<T2, E2>
           */
        using rvt = decltype(FUTURE_FUNC_RET_TYPES::type_t); /** return value type(T2) */
        using ret = decltype(FUTURE_FUNC_RET_TYPES::type_e); /** return error type(E2) */
        using promt = Promise<rvt, ret>; /** promise type */
        std::shared_ptr<promt> prom = std::make_shared<promt>();
        Future<rvt, ret> fu = prom->get_future();

        std::shared_ptr < Future<T, E> > curFu = new Future(std::move(mFuture), mInnerState);
        /** todo refineme, check mInnerState is valid?? */
        mInnerState->setCommunicationFunc([curFu, func, prom]() {

            Future<rvt, ret> fret = func(std::move(curFu.get()));
            Result<rvt, ret> res = fret.GetResult();
            if (res.HasValue())
            {
                prom->set_value(std::move(res.Value()));
            }
            else
            {
                prom->SetError(std::move(res.Error()));
            }
            });

        return fu;
    }
#endif

    template <typename F, class = typename std::enable_if<internal_impl::is_template_of<Result, std::result_of<F(std::move(*this))>::type>::value>::type >
    auto then(F&& func) -> decltype(Future< FUTURE_FUNC_RET_TYPES::type_t, FUTURE_FUNC_RET_TYPES::type_t>)
    {
        /** for the case of: Result<T2, E2> F(Future<T, E>&& f)
                   *  result of then() is: Future<T2, E2>
                   */
        using rvt = decltype(FUTURE_FUNC_RET_TYPES::type_t); /** return value type(T2) */
        using ret = decltype(FUTURE_FUNC_RET_TYPES::type_e); /** return error type(E2) */
        using promt = Promise<rvt, ret>; /** promise type */
        std::shared_ptr<promt> prom = std::make_shared<promt>();
        Future<rvt, ret> fu = prom->get_future();

        std::shared_ptr < Future<T, E> > curFu = new Future(std::move(mFuture), mInnerState);
        /** todo refineme, check mInnerState is valid?? */
        mInnerState->setCommunicationFunc([curFu, func, prom]() {

            Result<rvt, ret> res = func(std::move(curFu.get()));
            if (res.HasValue())
            {
                prom->set_value(std::move(res.Value()));
            }
            else
            {
                prom->SetError(std::move(res.Error()));
            }
            });

        return fu;
    }

    template <typename F, class = typename std::enable_if<!internal_impl::is_template_of<Result, std::result_of<F(std::move(*this))>::type>::value &&
                                                          !internal_impl::is_template_of<Future, std::result_of<F(std::move(*this))>::type>::value >::type >
    auto then(F&& func) -> decltype(Future<std::result_of<func(std::move(*this))>::type>)
    {
        /** for the case of: xxx  F(Future<T, E>&& f)
                   *  result of then() is: Future<xxx, E>
                   */
        using func_ret_type = decltype(func(std::move(*this)));
        using promt = Promise<func_ret_type, E>; /** promise type */
        std::shared_ptr<promt> prom = std::make_shared<promt>();
        Future<func_ret_type, E> fu = prom->get_future();

        std::shared_ptr < Future<T, E> > curFu = new Future(std::move(mFuture), mInnerState);
        /** todo refineme, check mInnerState is valid?? */
        mInnerState->setCommunicationFunc([curFu, func, prom]() {
            prom->set_value((func(std::move(curFu.get()))));
            });

        return fu;
    }

#endif

#if 0
    template <typename F>
    auto then(F&& func)
    {
        using func_ret_type = decltype(func(std::move(*this)));

        if (internal_impl::is_template_of<Future, func_ret_type >::value)
        { /** for the case of: Future<T2, E2> F(Future<T, E>&& f)
           *  result of then() is: Future<T2, E2>
           */
            using rvt = decltype(FUTURE_FUNC_RET_TYPES::type_t); /** return value type(T2) */
            using ret = decltype(FUTURE_FUNC_RET_TYPES::type_e); /** return error type(E2) */
            using promt = Promise<rvt, ret>; /** promise type */
            std::shared_ptr<promt> prom = std::make_shared<promt>();
            Future<rvt, ret> fu = prom->get_future();

            std::shared_ptr < Future<T, E> > curFu = new Future(std::move(mFuture), mInnerState);
            /** todo refineme, check mInnerState is valid?? */
            mInnerState->setCommunicationFunc([curFu, func, prom]() {
                    
                    Future<rvt, ret> fret = func(std::move(curFu.get()));
                    Result<rvt, ret> res = fret.GetResult();
                    if (res.HasValue())
                    {
                       prom->set_value(std::move(res.Value()));
                    }
                    else
                    {
                        prom->SetError(std::move(res.Error()));
                    }
                });

            return fu;

        }
        else if (internal_impl::is_template_of<Result, func_ret_type >::value)
        { /** for the case of: Result<T2, E2> F(Future<T, E>&& f) 
           *  result of then() is: Future<T2, E2>
           */
            using rvt = decltype(FUTURE_FUNC_RET_TYPES::type_t); /** return value type(T2) */
            using ret = decltype(FUTURE_FUNC_RET_TYPES::type_e); /** return error type(E2) */
            using promt = Promise<rvt, ret>; /** promise type */
            std::shared_ptr<promt> prom = std::make_shared<promt>();
            Future<rvt, ret> fu = prom->get_future();

            std::shared_ptr < Future<T, E> > curFu = new Future(std::move(mFuture), mInnerState);
            /** todo refineme, check mInnerState is valid?? */
            mInnerState->setCommunicationFunc([curFu, func, prom]() {

                Result<rvt, ret> res = func(std::move(curFu.get()));
                if (res.HasValue())
                {
                    prom->set_value(std::move(res.Value()));
                }
                else
                {
                    prom->SetError(std::move(res.Error()));
                }
                });

            return fu;


        }
        else
        {/** for the case of: xxx  F(Future<T, E>&& f) 
           *  result of then() is: Future<xxx, E>
           */

            using promt = Promise<func_ret_type, E>; /** promise type */
            std::shared_ptr<promt> prom = std::make_shared<promt>();
            Future<func_ret_type, E> fu = prom->get_future();

            std::shared_ptr < Future<T, E> > curFu = new Future(std::move(mFuture), mInnerState);
            /** todo refineme, check mInnerState is valid?? */
            mInnerState->setCommunicationFunc([curFu, func, prom]() {       
                    prom->set_value((func(std::move(curFu.get()))));   
                });

            return fu;
        }
    }
#endif

#endif

private:
    Future(Future const&) = delete;
    Future& operator= (Future const&) = delete;


private:
    friend class Promise<T, E>;

    explicit Future(future_type&& mf, state_ptr& innerState)
        :mFuture(std::move(mf)),
        mInnerState(innerState)
    {
        /** todo something */
    }

private:
    future_type mFuture;
    state_ptr mInnerState;
};


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

/**
 * This section defines the interface of the ara::core::Future<T,E> template specialization where the type T is void
 */
template <typename E>
class Future< void, E > final
{
public:
    using value_type = Result<void, E>;
    using future_type = std::future<value_type>;
    using internal_sate_type = internal_impl::FutureInternalState;
    using state_ptr = std::shared_ptr<internal_sate_type>;
public:
    Future() noexcept
    {
        /** todo something */
    }
    Future(Future&& other) noexcept
        :mFuture(std::move(other.mFuture)),
        mInnerState(std::move(other.mInnerState))
    {
        /** todo something */
    }

    ~Future ()
    {
        /** todo something */
    }

public:
    Future& operator= (Future&& other) noexcept
    {
        mFuture = std::move(other.mFuture);
        mInnerState = std::move(other.mInnerState);
    }

public:
    void get()
    {
        return GetResult().ValueOrThrow();
    }

    Result<void, E> GetResult() noexcept
    {
        try
        {
            return std::move(mFuture.get());
        }
        catch (std::future_error const& err)
        {
            const std::error_code& errc = err.code();
            future_errc ferr = future_errc::no_state;
            if (errc == std::future_errc::broken_promise)
            {
                ferr = future_errc::broken_promise;
            }
            else if (errc == std::future_errc::future_already_retrieved)
            {
                ferr = future_errc::future_already_retrieved;
            }
            else if (errc == std::future_errc::no_state)
            {
                ferr = future_errc::no_state;
            }
            else if (errc == std::future_errc::promise_already_satisfied)
            {
                ferr = future_errc::promise_already_satisfied;
            }
            else
            {
                /** tofo refine.*/
                //return value_type::FromError()
            }

            return std::move(value_type::FromError(ferr));
        }
    }

    bool valid() const noexcept
    {
        return mFuture.valid();
    }

    bool is_ready() const
    {
        return (mFuture.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready);
    }
public:
    void wait() const
    {
        mFuture.wait();
    }

    template <typename Rep, typename Period>
    future_status wait_for(std::chrono::duration< Rep, Period > const& timeoutDuration) const
    {
        return internal_impl::Convert2StdStatus(mFuture.wait_for(timeoutDuration));
    }

    template <typename Clock, typename Duration>
    future_status wait_until(std::chrono::time_point< Clock, Duration > const& deadline) const
    {
        return internal_impl::Convert2StdStatus(mFuture.wait_until(deadline));
    }

    /** now implmentation this member function only when c++ standard >= c++14,
     *  todo, fixme, is need implementation for c++11
     */
#if __cplusplus >= 201402L


     /**@brief Register a callable(callback function) that gets called when the Future becomes ready.
      * When func is called, it is guaranteed that get() and GetResult() will not block.
      * func may be called in the context of this call or in the context of Promise::set_value() or
      * Promise::SetError() or somewhere else.
      * The return type of then depends on the return type of func (aka continuation).

      *the F type maybe:
      * - Future<T2, E2> F(Future<T, E>&& f)
      * - Result<T2, E2> F(Future<T, E>&& f)
      * - xxx            F(Future<T, E>&& f)
      */

      /** register a communication function(only called once)   */
    template <typename F>
    auto then(F&& func)
    {
        using func_ret_type = decltype(f(std::move(*this)));

        if (internal_impl::is_template_of<Future, func_ret_type >::value)
        { /** for the case of: Future<T2, E2> F(Future<T, E>&& f)
           *  result of then() is: Future<T2, E2>
           */
            using rvt = decltype(FUTURE_FUNC_RET_TYPES::type_t); /** return value type(T2) */
            using ret = decltype(FUTURE_FUNC_RET_TYPES::type_e); /** return error type(E2) */
            using promt = Promise<rvt, ret>; /** promise type */
            std::shared_ptr<promt> prom = std::make_shared<promt>();
            Future<rvt, ret> fu = prom->get_future();

            std::shared_ptr < Future<void, E> > curFu = new Future(std::move(mFuture), mInnerState);
            /** todo refineme, check mInnerState is valid?? */
            mInnerState->setCommunicationFunc([curFu, func, prom]() {

                Future<rvt, ret> fret = func(std::move(curFu.get()));
                Result<rvt, ret> res = fret.GetResult();
                if (res.HasValue())
                {
                    prom->set_value(std::move(res.Value()));
                }
                else
                {
                    prom->SetError(std::move(res.Error()));
                }
                });

            return fu;

        }
        else if (internal_impl::is_template_of<Result, func_ret_type >::value)
        { /** for the case of: Result<T2, E2> F(Future<T, E>&& f)
           *  result of then() is: Future<T2, E2>
           */
            using rvt = decltype(FUTURE_FUNC_RET_TYPES::type_t); /** return value type(T2) */
            using ret = decltype(FUTURE_FUNC_RET_TYPES::type_e); /** return error type(E2) */
            using promt = Promise<rvt, ret>; /** promise type */
            std::shared_ptr<promt> prom = std::make_shared<promt>();
            Future<rvt, ret> fu = prom->get_future();

            std::shared_ptr < Future<void, E> > curFu = new Future(std::move(mFuture), mInnerState);
            /** todo refineme, check mInnerState is valid?? */
            mInnerState->setCommunicationFunc([curFu, func, prom]() {

                Result<rvt, ret> res = func(std::move(curFu.get()));
                if (res.HasValue())
                {
                    prom->set_value(std::move(res.Value()));
                }
                else
                {
                    prom->SetError(std::move(res.Error()));
                }
                });

            return fu;


        }
        else
        {/** for the case of: xxx  F(Future<T, E>&& f)
           *  result of then() is: Future<xxx, E>
           */

            using promt = Promise<func_ret_type, E>; /** promise type */
            std::shared_ptr<promt> prom = std::make_shared<promt>();
            Future<func_ret_type, E> fu = prom->get_future();

            std::shared_ptr < Future<void, E> > curFu = new Future(std::move(mFuture), mInnerState);
            /** todo refineme, check mInnerState is valid?? */
            mInnerState->setCommunicationFunc([curFu, func, prom]() {
                prom->set_value(std::move(func(std::move(curFu.get()))));
                });

            return fu;
        }
    }

#endif

private:
    Future (Future const &other)=delete;
    Future& operator= (Future const &other)=delete;

private:
    friend class Promise<void, E>;

    explicit Future(future_type&& mf, state_ptr& innerState)
        :mFuture(std::move(mf)),
        mInnerState(innerState)
    {
        /** todo something */
    }
private:
    future_type mFuture;
    state_ptr mInnerState;
};


} /** namespace core */
} /** namespace ara */

#endif /** !__CORE_COMPONENT_H__ */

